/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */

//
// WorldCreator.h
//

#ifndef __WORLDCREATOR_H
#define __WORLDCREATOR_H

enum INSTANCE_TYPE
{
	INSTANCE_NULL,
	INSTANCE_RAID,
	INSTANCE_NONRAID,
	INSTANCE_BATTLEGROUND,
	INSTANCE_MULTIMODE,
};

enum INSTANCE_ABORT_ERROR{
	INSTANCE_OK									= 0x00,
	INSTANCE_ABORT_ERROR_ERROR					= 0x01,
	INSTANCE_ABORT_FULL							= 0x02,
	INSTANCE_ABORT_NOT_FOUND					= 0x03,
	INSTANCE_ABORT_TOO_MANY						= 0x04,
	INSTANCE_ABORT_ENCOUNTER					= 0x06,
	INSTANCE_ABORT_NON_CLIENT_TYPE				= 0x07,
	INSTANCE_ABORT_HEROIC_MODE_NOT_AVAILABLE	= 0x08,
	INSTANCE_ABORT_UNIQUE_MESSAGE				= 0x09,
	INSTANCE_ABORT_TOO_MANY_REALM_INSTANCES		= 0x0A,
	INSTANCE_ABORT_NOT_IN_RAID_GROUP 			= 0x0B,
	INSTANCE_ABORT_REALM_ONLY					= 0x0F,
	INSTANCE_ABORT_MAP_NOT_ALLOWED				= 0x10
};

enum INSTANCE_RESET_ERROR
{
	INSTANCE_RESET_ERROR_PLAYERS_INSIDE = 0x00,
	INSTANCE_RESET_ERROR_MEMBERS_OFFLINE = 0x01,
	INSTANCE_RESET_ERROR_PLAYERS_ENTERING = 0x02
};

extern const char * InstanceAbortMessages[];

class Map;
class MapMgr;

class Object;
class Group;
class Player;
class Battleground;

class SERVER_DECL FormationMgr : public Singleton < FormationMgr >
{
	map<uint32, Formation*> m_formations;
public:
	typedef std::map<uint32, Formation*> FormationMap;
    FormationMgr();
	~FormationMgr();

	Formation * GetFormation(uint32 sqlid)
	{
		FormationMap::iterator itr = m_formations.find(sqlid);
		return (itr == m_formations.end()) ? 0 : itr->second;
	}
};

class SERVER_DECL Instance
{
public:
	uint32 m_instanceId;
	uint32 m_mapId;
	MapMgr * m_mapMgr;
	uint32 m_creatorGuid;
	uint32 m_creatorGroup;
	bool m_persistent;
	uint32 m_difficulty;
    set<uint32> m_killedNpcs;
	time_t m_creation;
	time_t m_expiration;
	MapInfo * m_mapInfo;
	bool m_isBattleground;

	void LoadFromDB(Field * fields);
	void SaveToDB();
	void DeleteFromDB();
};

typedef HM_NAMESPACE::hash_map<uint32, Instance*> InstanceMap; 

class SERVER_DECL InstanceMgr
{
	friend class MapMgr;
public:
	InstanceMgr();	
	~InstanceMgr();

	WoWICE_INLINE Map* GetMap(uint32 mapid)
	{
		if(mapid>=NUM_MAPS)
			return NULL;
		else
			return m_maps[mapid];
	}

	uint32 PreTeleport(uint32 mapid, Player * plr, uint32 instanceid);
	MapMgr * GetInstance(Object* obj);
	uint32 GenerateInstanceID();
	void BuildXMLStats(char * m_file);
	void Load(TaskList * l);

	// deletes all instances owned by this player.
	void ResetSavedInstances(Player * plr);

	// deletes all instances owned by this group
	void OnGroupDestruction(Group * pGroup);

	// player left a group, boot him out of any instances he's not supposed to be in.
	void PlayerLeftGroup(Group * pGroup, Player * pPlayer);

	// has an instance expired?
	// can a player join?
    WoWICE_INLINE bool PlayerOwnsInstance(Instance * pInstance, Player * pPlayer)
	{
		// Expired?
		if( pInstance->m_expiration && (UNIXTIME+20) >= pInstance->m_expiration)
		{
			_DeleteInstance(pInstance, true);
			return false;
		}

		// Persistent instance handling
		if(pInstance->m_persistent)
		{
			return (pPlayer->GetPersistentInstanceId(pInstance->m_mapId, pInstance->m_difficulty) == pInstance->m_instanceId);
		}
		// Default instance handling
		else if((pPlayer->GetGroup() && pInstance->m_creatorGroup == pPlayer->GetGroup()->GetID()) || pPlayer->GetLowGUID() == pInstance->m_creatorGuid)
			return true;

		return false;
	}

	// has an instance expired?
	WoWICE_INLINE bool HasInstanceExpired(Instance * pInstance)
	{
		// expired?
		if( pInstance->m_expiration && (UNIXTIME+20) >= pInstance->m_expiration)
			return true;

		return false;
	}

	// check for expired instances
	void CheckForExpiredInstances();

	// delete all instances
	void Shutdown();

	// packets, w000t! we all love packets!
	void BuildRaidSavedInstancesForPlayer(Player * plr);
	void BuildSavedInstancesForPlayer(Player * plr);
	MapMgr * CreateBattlegroundInstance(uint32 mapid);

	// Create an instance for Level 3 gm command
	MapMgr * CreateInstance(uint32 instanceType, uint32 mapid);

	// A (should be) safe way for scripts to delete an active instance
	void SafeDeleteInstance(MapMgr * mgr);

	// this only frees the instance pointer, not the mapmgr itself
	void DeleteBattlegroundInstance(uint32 mapid, uint32 instanceid);
	MapMgr* GetMapMgr(uint32 mapId);

	bool InstanceExists(uint32 mapid, uint32 instanceId)
	{
		return GetInstanceByIds(mapid, instanceId) != NULL;
	}

	Instance* GetInstanceByIds(uint32 mapid, uint32 instanceId)
	{
		if(mapid > NUM_MAPS)
			return NULL;
		if(mapid == NUM_MAPS)
		{
			Instance *in;
			for(uint32 i=0; i<NUM_MAPS; ++i)
			{
				in = GetInstanceByIds(i, instanceId);
				if(in != NULL)
					return in;
			}
			return NULL;
		}
		InstanceMap *map = m_instances[mapid];
		if(map == NULL)
			return NULL;
		InstanceMap::iterator instance = map->find(instanceId);
		return instance == map->end() ? NULL : instance->second;
	}

private:
	void _LoadInstances();
	void _CreateMap(uint32 mapid);
	MapMgr* _CreateInstance(Instance * in);
	MapMgr* _CreateInstance(uint32 mapid, uint32 instanceid);		// only used on main maps!
	bool _DeleteInstance(Instance * in, bool ForcePlayersOut);

	uint32 m_InstanceHigh;

	Mutex m_mapLock;
	Map * m_maps[NUM_MAPS];
	InstanceMap* m_instances[NUM_MAPS];
	MapMgr * m_singleMaps[NUM_MAPS];
	time_t m_nextInstanceReset[NUM_MAPS];
};

extern SERVER_DECL InstanceMgr sInstanceMgr;

#endif
